#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G20 -- S17 ------------------------------------------------------------##
__all__ = []

from .. import autostacks
from .. import pilelib as stack
from .. import stackfill as fill
from ..formlib import Layout
from ..gamelib import info as gi, state, game
from ..hint import DefaultHint, CautiousDefaultHint, DropbyCautiousHint
from ..packlib import info as cs, place
from ..pilelib import move as a_move
from ..utillib.misc import kwdefault
from . import unionsquare, golf

GT, SL = gi.GT, gi.SL
StackWrapper = stack.StackWrapper


class Colonel_Hint(DefaultHint):

    def _get_move_card_bonus(self, from_stack, to_stack, pile, rpile):
        """
        _get_move_card_bonus(from_stack:obj, to_stack:obj, pile:tup, rpile:tup)
          -> bool

        Score card play (drop + 1st move):

        RowStack to RowStack move without gap score 0, then parent scoring.

        :param Stack from_stack: card origin
        :param Stack to_stack: card destination
        :param tuple pile: cards of origin
        :param tuple rpile: cards of destination
        :return: bonus score (0 .. 999)
        """
        rows = self._stacks['set']['rows']
        if rpile and all(s in rows for s in (from_stack, to_stack,)):
            return 0
        return super(Colonel_Hint, self)._get_move_card_bonus(from_stack,
                                                        to_stack, pile, rpile)


class Frames_Hint(CautiousDefaultHint):

    def step050(self, reserves, rows):
        """
        step050(reserves:list(Stack ...), rows:list(Stack ...)) -> None

        Calculate and score **reserves** build on **rows**. (10000 .. 19999)

        Augment with **reserves** builds **rows** only when **talon** has cards.
        Increase initial score to sum with **score_move_pile** then **add_hint**
        with for first possible move.
        """
        if not reserves or not self._stacks['set']['talon'].cards:
            return
        args = self.pile_builds(from_stacks=reserves, to_stacks=rows)
        if args:
            self._score = 5000
            self.score_move_pile(*args)
            self.add_hint(len(args[2]), args[0], args[1])


class ShadyLanes_Hint(CautiousDefaultHint):

    def step050(self, reserves, rows):
        """
        step050(reserves:list(Stack ...), rows:list(Stack ...)) -> None

        Calculate and score **reserves** build on **rows**. (10000 .. 19999)

        Augment with **reserves** builds **rows** only when **talon** has cards.
        Increase initial score with consideration of **rows** top card to sum
        with **score_move_pile** then **add_hint** with for first possible move.
        """
        if not reserve or not self._stacks['set']['talon'].cards:
            return
        args = self.pile_builds(from_stacks=reserves, to_stacks=rows)
        if args:
            self._score = 5000 - args[1]['rank']
            self.score_move_pile(*args)
            self.add_hint(len(args[2]), args[0], args[1])


class BC_BuildMethod(object):
    """
    Build from row below. Base on bottom row 1 card from _waste_.
    """

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:seq) -> bool

        Checks parent *capability* for cards. Cards play from _row_ pile below
        this or if bottom _waste_.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean
        """
        if not super(BC_BuildMethod, self).cardsBuild(from_stack, cards):
            return False
        rows = self.game.s['rows']
        lrows, step = len(rows), 8
        for i, j in range(0, lrows - step, step), range(0, lrows, step):
            if self in rows[i:j] and from_stack in rows[j:j + step]:
                return True
        return (self in rows[lrows - step:] and
                from_stack in self.game.s['wastes'])


class TwilightZone_Talon(stack.FaceUp_StackMethods, stack.OpenDragMethods,
                         stack.OpenAutoMethods, stack.WasteTalon):
    """
    Build None. Play top. Gap reset _waste_.
    """
    pass


class DealWasteRedealTalon(stack.DealRowRedealTalon):
    """
    Deal 1 card on _waste_ with many piles in group. Reset _waste_ when gap.
    """

    @property
    def _piles(self):
        """
        _piles -> tup

        :return: deal Game.s.waste
        :rtype: tuple
        """
        return self.game.s['xwastes']

    def deal_cards(self, sound=False, shuffle=False):
        """
        deal_cards([sound:bool=False]) -> None

        Deal cards for Game.talon_deal() to other piles. Deal cards to
        Game.s.rows when with cards and then pick up them when without cards.

        :keyword boolean sound: sound fx?
        """
        ncards = 0
        game = self.game
        if self.cards:
            if sound and game.can_play_sound:
                game._play_sound('dealwaste')
            for i in range(self.num_cards):
                ncards += self.dealRowAvail(sound=False, frames=4)
        else:
            if sound and game.can_play_sound:
                game._play_sound('turnwaste', priority=20)
            ncards += self._redeal(rows=self._piles, frames=4)
            if shuffle:
                game._move_do(a_move.AShuffleStackMove(self, game.random)
                              )  # low level
            game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
            ncards = self.dealRowAvail(sound=False, frames=4)
        return ncards


class TwilightZone_Waste(stack.FromTalonBuildMethod, stack.WasteStack):
    pass


class Frames_Foundation(stack.FromRowStackBuildMethod,
                        unionsquare.Foundation):
    """
    Build up around in rank by same suit from _row_.
    """
    pass


class TwilightZone_Foundation(stack.FromRowStackTalonBuildMethod,
                              stack.AC_Foundation):
    """
    Build up in rank by alt color from any but _reserve_ or _waste_.
    """
    pass


class RoyalCotillion_Foundation(stack.SS_Foundation):
    """
    Build up in rank by same suit. Set _BOTTOM_IMAGE_ as rank when
    cap['base_rank'] is TWO.
    """

    def getBottomImage(self):
        """
        getBottomImage() -> obj

        Overide to use rank bottom image if base_rank is TWO otherwise use
        parent method of using _BOTTOM_IMAGE_ to Images.base().

        :return: pile's rank or suit base image.
        :rtype: Image
        """
        if self.cap['base_rank'] == cs.TWO:
            return self.game.images.bottom('rank', cs.TWO)
        return super(RoyalCotillion_Foundation, self).getBottomImage()


class BritishConstitution_Foundation(stack.FromBuildMethod,
                                     stack.SS_Foundation):
    """
    Build up in rank by same suit _row_ pile [1..8].
    """

    @property
    def _isInFromStacks(self):
        """
        Piles cards must be played from 1st _row_ (top of block).

        :param object from_stack: pile that held sequence.
        :param sequence cards: sequence.
        :return: sequence accepted?
        :rtype: boolean
        """
        return self.game.s['rows'][:8]


class Colonel_RowStack(stack.SS_RowStack):
    """
    Build down in rank by same suit from S with cards else S row. Gap N play
    1st. Play S no cards. _BOTTOM_IMAGE_ is shade.
    """
    _BOTTOM_IMAGE_ = 'shade'

    def _getStackIndex(self, stack):
        """
        _getStackIndex(stack:obj) -> tup

        Get row and index of stack.

        :param object stack: pile to find row and index.
        :return: row and index of pile?
        :rtype: tuple of row, index
        """
        index = list(self.game.s['rows']).index(stack)
        return index / 12, index

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:seq) -> bool

        Checks parent *capability* for cards. Cards fill N pile gap 1st.
        Cards from _waste_ if pile S row or gap. Cards from S pile if has
        cards, else rows S.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.builds for the *capability* check.
        """
        if not super(Colonel_RowStack, self).cardsBuild(from_stack, cards):
            return False
        rows = self.game.s['rows']
        to_index, to_row = self._getStackIndex(self)
        if to_row in (1, 2) and not rows[to_index - 12].cards:  # N has gap?
            return False
        stack = rows[to_index + 12] if to_row in (0, 1) else None  # S pile?
        if from_stack in self.game.s['wastes']:
            return not stack or (stack and not stack.cards)   # S pile or gap
        if stack and stack.cards:  # S pile cards and from_stack?
            return from_stack is stack
        from_index, from_row = self._getStackIndex(from_stack)
        return from_row > to_row  # S from_stack?

    def cardsPlay(self, cards):
        """
        cardsPlay(cards:tup) -> bool

        Checks parent *capability* for cards. Cards play when S pile has no
        cards.

        :param tuple cards: cards to move from this pile
        :return: cards moveable?
        :rtype: boolean
        """
        if not super(Colonel_RowStack, self).cardsPlay(cards):
            return False
        to_index, to_row = self._getStackIndex(self)
        return not (to_row in (0, 1) and
                    self.game.s['rows'][to_index + 12].cards)


class Frames_RowStack(stack.UD_SS_RowStack):
    """
    Build either in rank by same suit from any _row_ or inline _reserve_.
    """

    def _isSameLine(self, stack):
        """
        _isSameLine() -> bool

        Pile of _reserve_ in same row or col as _row_.
        """
        s = self.game.s
        row, reserve = s['rows'].index(self), s['reserves'].index(stack)
        l = 4 if row <= 8 else 3
        return (0 if row <= 8 else 8) / l == reserve / l

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:seq) -> bool

        Checks parent *capability* for cards. Card from _row_ or inline
        _reserve_ pile.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.builds for the *capability* check.
        """
        if not super(Frames_RowStack, self).cardsBuild(from_stack, cards):
            return False
        return (from_stack in self.s['rows'] or
              (from_stack in self.s['reserves'] and self.isSameLine(from_stack)))


class BritishConstitution_RowStack(BC_BuildMethod, stack.AC_RowStack):
    """
    Build down in rank by alt color from row below. Base on bottom row 1 card
    from _waste_.
    """
    pass


class NewBritishConstitution_RowStack(BC_BuildMethod, stack.RK_RowStack):
    """
    Build down in rank alone from row below. Base on bottom row 1 card from
    _waste_.
    """
    pass


class ShadyLanes_RowStack(stack.GapFromReserveBuildMethod, stack.AC_RowStack):
    """
    Build down in rank by alt color. Base from _reserve_.
    """
    pass


class TwilightZone_RowStack(stack.FromWasteBuildMethod, stack.AC_RowStack):
    """
    Build down in rank by alt color from _waste_.
    """
    pass


class TheRedAndTheBlack_Foundation(stack.FromRowStackWasteBuildMethod,
                                   stack.AC_Foundation):
    """
    Build down in rank by alt color from _row_ or _waste_.
    """
    pass


class TheRedAndTheBlack_RowStack(stack.AC_RowStack):
    """
    Build down in rank by alt color. _BOTTOM_IMAGE_  is shade.
    """
    _BOTTOM_IMAGE_ = 'shade'


class SS_Reserve(stack.ReserveStack):
    """
    Build 1 card. Play top. _BOTTOM_IMAGE_  is shade.
    """
    _BOTTOM_IMAGE_ = 'suit'


class fromWaste_Reserve(stack.FromWasteBuildMethod, stack.ReserveStack):
    """
    Build 1 card from _waste_.  Play top.
    """
    pass


class RoyalCotillion(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz NE. Init deal 1 card. Build none. Play top.

    _reserve_
      Pile 16 over 4 rows NW. Init deal 1 card. Build none. Play top. Gap deal
      1 card from _waste_.

    _talon_
      Pile 1 SW. Deal _waste_ 1 card.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 over 2 cols N. Build up in rank by same suit by 2. W from base
      ACE; E base from TWO. Play top.

    note::
      ⟪highlight_play⟫ disabled, ⟪autodrop⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundations_: RoyalCotillion_Foundation (dir=2, mod=13),
      RoyalCotillion_Foundation (dir=2, mod=13, base_rank=TWO)
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserve_: stack.OpenStack
    """
    __info__ = dict(id=54, name='Royal Cotillion', game_type=(GT.TWO_DECK |
                    GT.XPAT), decks=2, skill_level=SL.LUCK, version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundations_ = (StackWrapper(RoyalCotillion_Foundation, dir=2, mod=13),
                     StackWrapper(RoyalCotillion_Foundation, dir=2, mod=13,
                                  base_rank=cs.TWO),)
    _row_ = stack.BasicRowStack
    _reserve = stack.OpenStack

    def _create_game(self, rows=4, reserves=(4,) * 4, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + (rows + l.NDECKS + max(reserves)) * l.XS,
                     l.YM + max(l.NSUIT, len(reserves) * l.YS))
        x, y = l.XM, l.YM
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        dx = x
        for suit in l.SUIT:  # Vert
            x = dx
            for _foundation_ in self._foundations_:  # Horz
                s['foundations'].append(_foundation_(x, y, game=self, suit=suit))
                x += l.XS
            y += l.YS
        dx = x
        for reserve in reserves:  # Vert
            x = dx
            for i in range(reserve):  # Horz
                s['reserves'].append(self._reserve_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = l.XM + (rows - 2) / 2 * l.XS, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, self))
        l.set_ncards_text(s['wastes'][-1], 'se')
        l.defaultStackGroups()
        return l

    def _start(self, rows=4, **kw):
        self.s['talon'].dealRow(rows=self.s['reserves'], frames=0)
        self._play_sound('deal')
        for i in range(rows):
            self.s['talon'].dealRow()
        self.s['talon'].deal_cards()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _reserve_ when gap from _waste_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromWaste(self, stack, self.s['reserves'])

    @property
    def _highlight_piles(self):
        """
        Return piles to highlight for possible move.

        :return: empty.
        :rtype: tuple
        """
        return ()

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autodrop when not event.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        piles = super(RoyalCotillion, self)._autopiles(event)
        return autostacks.flip_quickplay(piles) if event is None else piles


class OddAndEven(RoyalCotillion):
    """
    Finish cards on _foundation_ within 2 turns.

    _reserve_
      Pile 9 over 2 rows. Init deal 1 card. Build none. Play top.

    _talon_
      Pile 1 SW. Deal _waste_ 1 card.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 over 2 cols N _reserve_. Build up in rank by same suit by 2. W
      from base ACE; E base from TWO. Play top.

    note::
      ⟪highlight_play⟫ disabled, ⟪autodrop⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundations_: RoyalCotillion_Foundation (dir=2, mod=13),
      RoyalCotillion_Foundation (dir=2, mod=13, base_rank=TWO)
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserve_: stack.OpenStack
    """
    __info__ = dict(id=55, name='Odd and Even', game_type=(GT.TWO_DECK |
                    GT.CHILDREN), decks=2, redeals=1, skill_level=SL.LUCK,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = StackWrapper(stack.WasteTalon, max_rounds=2)

    def _create_game(self, reserves=(4, 5,), **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + (l.NDECKS + max(reserves)) * l.XS,
                     l.YM + (max(l.NSUIT, len(reserves)) + 1) * l.YS)
        dx, y, = l.XM, l.YM
        for _foundation_ in self._foundations_:  # Vert
            x = dx
            for suit in l.SUIT:  # Horz
                s['foundations'].append(_foundation_(x, y, game=self, suit=suit))
                x += l.XS
            y += l.YS
        dx = x
        y += l.YS
        for reserve in reserves:  # Vert
            x = dx
            for i in range(reserve):  # Horz
                s['reserves'].append(self._reserve_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = l.XM, self.height - l.YS
        s['talon'] = self.talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        l.set_rounds_text(s['talon'], 'nnn')
        x = x + l.XS
        s['wastes'].append(self.waste(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'n')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].deal_cards()


class Kingdom(RoyalCotillion):
    """
    Finish cards on _foundation_ within a turn.

    _reserve_
      Pile 8 horz S _foundation. Init deal 1 card. Build none. Play top.

    _talon_
      Pile 1 S. Deal _waste_ 1 card.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 horz N. Init deal base ACE 1. Build up in rank alone. Play top.

    note::
      ⟪highlight_play⟫ disabled, ⟪autodrop⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserve_: stack.OpenStack
    """
    __info__ = dict(id=143, name='Kingdom', game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.MOSTLY_LUCK, version='3.21')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundation_ = stack.RK_Foundation

    def _create_game(self, reserves=8, **kw):
        l, s = Layout(self), self.s
        max_rows = max(reserves, l.NSUITS)
        self.table_size = (l.XM + max_rows * l.XS,
                     l.YM + (l.NDECKS + 2) * l.YS)
        center = lambda p: l.XM + (max_rows - p) * l.XS / 2
        x, y, = center(l.NSUITS), l.YM
        for i in l.SUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self))
            x += l.XS
        x = center(reserves)
        y += l.YS
        for i in range(reserves):  # Horz
            s['reserves'].append(self._reserve_(x, y, game=self))
            x += l.XS
        x, y = center(2), self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, self))
        l.set_ncards_text(s['wastes'][-1], 'se')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return single ACE on top for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce, ncards=1)

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=[self.s['foundations'][0]])
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].deal_cards()


class ThreePirates(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 horz. Init deal 4 open card. Build down in rank by same suit.
      Play top.

    _talon_
      Pile 1 S. Deal _waste_ 1 card.

    _waste_
      Pile 3 horz E _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 horz N _row_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: DealWasteRedealTalon
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.SS_RowStack (max_move=1)
    :cvar Stack _reserve_: stack.WasteStack
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=579, name='Three Pirates', game_type=GT.TWO_DECK,
                    decks=2, skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = DealWasteRedealTalon
    _waste_ = stack.WasteStack
    _foundations_ = (stack.SS_Foundation,) * 2
    _row_ = StackWrapper(stack.SS_RowStack, max_move=1)
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=10, wastes=3, playcards=16, **kw):
        l, s = Layout(self), self.s
        max_rows = max(rows, l.NSUITS)
        self.table_size = (l.XM + max_rows * l.XS,
                  l.YM + (l.NDECKS + 1) * l._get_stack_height(playcards, l.YS))
        center = lambda p: l.XM + (max_rows - p) * l.HXS
        x, y, = center(l.NSUITS), l.YM
        for _foundation_ in self._foundations_:  # Horz
            for suit in l.NSUITS:
                s['foundations'].append(_foundation_(x, y, game=self, suit=suit))
                x += l.XS
        x = center(rows)
        y += l.YS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self, offset=(0, l.YOFFSET)))
            x += l.XS
        x, y = center(1 + wastes), self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw' if wastes == 1 else 'n')
        l.set_rounds_text(s['talon'], 'nn' if wastes > 1 else 'nnn',
                          always=False)
        x += l.XS
        for i in range(wastes):  # Horz
            s['xwastes'].append(self._waste_(x, y, game=self, offset=(0, 0)))
            l.set_ncards_text(s['xwastes'][i], 'se' if wastes == 1 else 'n')
            x += l.XS
        l.defaultStackGroups()
        return l

    def _start(self, rows=4, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class Alhambra(ThreePirates):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 8 horz S _foundation_. Init deal 4 open cards. Build none. Play
      top.

    _talon_
      Pile 1 S. Deal _waste_ 1 card.

    _waste_
      Pile 1 E _talon_. Init deal a. Build either in rank by same suit wrap.
      Play top.

    _foundation_
      Pile 8 horz N. Init deal base ACE W 4, KING E 4. Build in rank
      by same suit: up E; down W. Play none.

    seealso::
      <http://en.wikipedia.org/wiki/Alhambra_%28solitaire%29>
    ------
    :cvar Stack _talon_: DealWasteRedealTalon (max_rounds=3)
    :cvar Stack _foundations_: stack.SS_Foundation (max_move=0),
      stack.SS_Foundation (max_move=0, base_rank=KING, dir=-1)
    :cvar Stack _row_: golf.UD_SS_Waste (mod=13, max_move=1)
    :cvar Stack _reserve_: stack.OpenStack
    :cvar Hint _hint_: hint.DropbyCautiousHint
    """
    __info__ = dict(id=234, name='Alhambra', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.BALANCED, version='4.60')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = StackWrapper(DealWasteRedealTalon, max_rounds=3)
    _waste_ = StackWrapper(golf.UD_SS_Waste, mod=13, max_move=1)
    _foundations_ = (StackWrapper(stack.SS_Foundation, max_move=0),
                     StackWrapper(stack.SS_Foundation, max_move=0,
                     base_rank=cs.KING, dir=-1),)
    _row_ = stack.OpenStack
    _hint_ = DropbyCautiousHint

    def _create_game(self, **kw):
        kwdefault(kw, rows=8, wastes=1, playcards=9)
        l, s = super(Alhambra, self)._create_game(**kw), self.s
        self.sg['dropstacks'] += s['xwaste']
        self.sg['openstacks'] += s['xwaste']
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING of suit on top by suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, *kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        kw.setdefault('rows', 4)
        super(Alhambra, self)._start(*kw)


class Granada(Alhambra):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 4 horz S _foundation_. Init deal 4 open cards. Build none. Play
      top.

    _talon_
      Pile 1 S. Deal _waste_ 1 card.

    _waste_
      Pile 4 horz E _talon_. Init deal 1 card. Build either in rank by same
      suit wrap. Play top.

    _foundation_
      Pile 8 horz N. Init deal base ACE W 4, KING E 4. Build in rank
      by same suit: up E; down W. Play none.
    ------
    :cvar Stack _talon_: DealWasteRedealTalon (max_rounds=3)
    :cvar Stack _foundations_: stack.SS_Foundation (max_move=0),
      stack.SS_Foundation (max_move=0, base_rank=KING, dir=-1)
    :cvar Stack _row_: golf.UD_SS_Waste (mod=13, max_accept=1)
    :cvar Stack _reserve_: stack.OpenStack (max_accept=0)
    :cvar Hint _hint_: hint.DropbyCautiousHint
    """
    __info__ = dict(id=465, name='Granada', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.BALANCED, version='0.92fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('rows', 4)
        return super(Granada, self)._start(*kw)


class Reserves(Granada):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 4 horz S _foundation_. Init deal 12 open cards. Build none. Play
      top.

    _talon_
      Pile 1 S. Deal _waste_ 1 card.

    _waste_
      Pile 1 E _talon_. Init deal a. Build either in rank alone wrap. Play
      top. Gap none.

    _foundation_
      Pile 8 horz N. Init deal base ACE W 4, KING E 4. Build in rank
      by same suit: up E; down W. Play none.
    ------
    :cvar Stack _talon_: DealWasteRedealTalon (max_rounds=3)
    :cvar Stack _foundations_: stack.SS_Foundation (max_move=0),
      stack.SS_Foundation (max_move=0, base_rank=KING, dir=-1)
    :cvar Stack _row_: golf.UD_RK_Waste (base_rank=GAP, mod=13, max_move=1)
    :cvar Stack _reserve_: stack.OpenStack (max_accept=0)
    :cvar Hint _hint_: hint.DropbyCautiousHint
    """
    __info__ = dict(id=752, name='Reserves', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.BALANCED, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _waste_ = StackWrapper(golf.UD_RK_Waste, base_rank=cs.GAP, mod=13,
                           max_move=1)

    def _create_game(self, **kw):
        kw.setdefault('playcards', 11)
        return super(Reserves, self)._create_game(**kw)

    def _start(self, **kw):
        kw.setdefault('rows', 12)
        super(Reserves, self)._start(*kw)


class GrantsReinforcement(Reserves):
    """
    Finish cards on _foundation_ within 3 turns.

    _reserve_
      Pile 4 horz S _foundation_. Init deal 12 open cards. Build none. Play
      top. Gap deal from _talon_.

    _talon_
      Pile 1 S. Deal _waste_ 1 card.

    _row_
      Pile 1 E _talon_. Init deal 1 card. Build either in rank by same suit
      wrap. Play top. Gap none.

    _foundation_
      Pile 8 horz N. Init deal base ACE W 4, KING E 4. Build in rank
      by same suit: up E; down W. Play none.
    ------
    :cvar Stack _talon_: DealWasteRedealTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundations_: stack.SS_Foundation (max_move=0),
      stack.SS_Foundation (max_move=0, base_rank=KING, dir=-1)
    :cvar Stack _row_: golf.UD_SS_Waste (base_rank=GAP, mod=13, max_move=1)
    :cvar Stack _reserve_: stack.OpenStack (max_accept=0)
    :cvar Hint _hint_: hint.DropbyCautiousHint
    """
    __info__ = dict(id=609, name='Grantʼs Reinforcement',
                    game_type=GT.TWO_DECK, decks=2, redeals=2,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = StackWrapper(golf.UD_SS_Waste, base_rank=cs.GAP, mod=13, max_move=1)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _reserve_ when gap from _waste_.

        *Parameters*:
           stack : Stack
             pile just played
        """
        fill.fromTalon(self, stack, self.s['reserves'])


class BritishConstitution(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 32 over 4 rows. Init deal 1 card. Build down in rank by alt color
      from row below. Play Top. Gap bottom row 1 card from _waste_.

    _talon_
      Pile 1 NW. Deal _waste_ 1 card.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build None. Play top.

    _foundation_
      Pile 8 Horz N _row_. Init deal base ACE 8. Build up in rank by same
      suit from top row of _row_. Play top.

    seealso::
      <http://en.wikipedia.org/wiki/British_Constitution_%28solitaire%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: BritishConstitution_Foundation
    :cvar Stack _row_: BritishConstitution_RowStack (max_move=1)
    """
    __info__ = dict(id=391, name='British Constitution',
                    game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.BALANCED, ranks=tuple(range(cs.JACK + 1)),
                    altnames=('Constitution',), version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = BritishConstitution_Foundation
    _row_ = StackWrapper(BritishConstitution_RowStack, max_move=1)

    def _create_game(self, rows=32, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + 9 * l.XS, l.YM + 5 * l.YS)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        y += l.YS + l.TEXT_HEIGHT
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 's')
        dx = x + l.XS
        x, y = dx, l.YM
        for suit in l.SUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
            x += l.XS
        y += l.YS
        for i in range(rows / 8):  # Vert
            x = dx
            for j in range(8):  # Horz
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ [24-] when gap from _waste_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'][24:])


class NewBritishConstitution(BritishConstitution):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 32 over 4 rows. Init deal 1 card. Build down in rank alone
      from row below. Play Top. Gap bottom row 1 card from _waste_. Gap base
      JACK.

    _talon_
      Pile 1 NW. Deal _waste_ 1 card.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build None. Play top.

    _foundation_
      Pile 8 Horz N _row_. Init deal base ACE 8. Build up in rank by same
      suit from top row of _row_. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: BritishConstitution_Foundation
    :cvar Stack _row_: BritishConstitution_RowStack (base_rank=JACK,
      max_move=1)
    """
    __info__ = dict(id=392, name='New British Constitution',
                    game_type=(GT.TWO_DECK | GT.ORIGINAL), decks=2,
                    skill_level=SL.BALANCED, version='0.90fc',
                    ranks=tuple(range(cs.JACK + 1)))
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = StackWrapper(NewBritishConstitution_RowStack,
                         base_rank=cs.JACK, max_move=1)


class Carpet(game.Game):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 20 over 4 rows. Init deal 1 card. Build 1 card. Play top.

    _talon_
      Pile 1 NW. Deal _waste_ 1 card.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 4 over 2 col E W _row_. Init deal base ACE 4. Build up in rank by
      same suit. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundations_: stack.SS_Foundation
    :cvar Stack _row_: stack.ReserveStack
    """
    __info__ = dict(id=97, name='Carpet', game_type=(GT.ONE_DECK |
                    GT.CHILDREN), decks=1, version='3.00',
                    skill_level=SL.MOSTLY_LUCK)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SS_Foundation
    _row_ = stack.ReserveStack

    def _create_game(self, rows=(5,) * 4, **kw):
        l, s = Layout(self), self.s
        self.table_size = (4 * l.XM + (max(rows) + 3) * l.XS,
                     l.YM + len(rows) * l.YS)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        y += l.YS
        s['wastes'][-1] = self._waste_(x, y, game=self)
        l.set_ncards_text(s['wastes'][-1], 'se')
        dx, ox = x + l.XS + l.XM, max(rows) * l.XS + 2 * l.XM
        for suits in (l.SUITS[:2], l.SUITS[2:],):  # Vert
            x = dx
            for suit in suits:  # Horz
                s['foundations'].append(self._foundation_(x, y, game=self,
                                                       suit=suit))
                x += ox
            y += l.YS
        dx, y = 3 * l.XM + 2 * l.XS, l.YM
        for row in rows:  # Vert
            x = dx
            for i in range(row):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'])
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class Twenty(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Init deal Pile 20 over 2 rows 1 card. Build none. Play top. Gap 1 card
      from _talon_ else top.

    _talon_
      Pile 1 NW. Deal _row_ 1 card.

    _foundation_
      Pile 8 horz NE. Build up in rank by same suit. W from base
      ACE; E base from KING. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _foundations_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: stack.BasicRowStack (max_accept=1, max_cards=1)
    """
    __info__ = dict(id=443, name='Twenty', game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.BALANCED, version='0.92fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.DealRowTalon
    _foundations_ = (stack.SS_Foundation,
                     StackWrapper(stack.SS_Foundation, base_rank=cs.KING,
                                  dir=-1),)
    _row_ = StackWrapper(stack.BasicRowStack, max_accept=1, max_cards=1)

    def _create_game(self, rows=(10,) * 2, playcards=5, **kw):
        l, s = Layout(self), self.s
        dy = l.YS + playcards * l.YOFFSET
        self.table_size = (l.XM + max(max(rows), l.NSUITS + 1) * l.XS,
                     l.YM + l.YS + 2 * dy)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        x = self.width - l.NSUITS * l.XS
        for _foundation_ in self._foundations_:
            for suit in l.SUITS:  # Horz
                s['foundations'].append(_foundation_(x, y, game=self, suit=suit))
                x += l.XS
        y += l.YS
        for row in rows:  # Vert
            x = l.XM
            for i in range(row):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += self.height - dy
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and king of suit on top by suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class Frames(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 14 side of _reserve_. Init deal 1 card. Build either in
      rank by same suit. Play top.

    _reserve_
      Pile 12 over 3 rows. Init deal 1 card. Build none. Play top on
      pile of same col or row on _row. Gap 1 card from _waste_.

    _talon_
      Pile 1 NW. Deal _waste_ 1 card.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 4 corners of _reserve_. Build round in rank by same suit.
      Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: Frames_Foundation (dir=0, max_cards=26)
    :cvar Stack _row_: Frames_RowStack
    :cvar Stack _reserve_: stack.OpenStack
    :cvar Hint _hint_: Frames_Hint
    """
    __info__ = dict(id=608, name='Frames', game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = StackWrapper(Frames_Foundation, dir=0, max_cards=26)
    _row_ = Frames_RowStack
    _reserve_ = stack.OpenStack
    _hint_ = Frames_Hint

    def _create_game(self, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + 8 * l.XS, l.YM + 5 * l.YS)
        ddx, ddy = l.XM + 2 * l.XS, l.YM
        for i, dx, dy in zip(l.NSUITS, [0, 5] * 2, [0] * 2 + [4] * 2):
            x, y = l.delta_coords(x=ddx, y=ddy, dx=dx, dy=dy)
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
        rows = zip(zip(range(1, 5), [0] * 4) + zip(range(1, 5), [4] * 4))
        cols = zip(zip([0] * 4, range(1, 4)) + zip([5] * 4, range(1, 4)))
        for dx, dy in rows + cols:
            x, y = l.delta_coords(x=ddx, y=ddy, dx=dx, dy=dy)
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
        for dy in range(1, 4):  # Vert
            for dx in range(1, 5):  # Horz
                x, y = l.delta_coords(x=ddx, y=ddy, dx=dx, dy=dy)
                s['reserves'].append(self._reserve_(x, y, game=self))
        x, y, = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].deal_cards()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _reserve_ when gap from _waste_.

        *Parameters*:
           stack : Stack
             pile just played
        """
        fill.fromWaste(self, stack, self.s['reserves'])


class RoyalRendezvous(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 16 over 2 rows S. Init deal 1 card. Build none. Play top. Gap 1
      card from _waste_.

    _talon_
      Pile 1 NW. Deal _waste_ 1 card.

    _waste_
      Pile 1 S of _talon_. Build none. Play top.

    _foundation_
      Pile 16 over 2 rows N. Init deal base ACE N S 8, TWO SW SE 4. Build up
      in rank by same suit. NW NE only KING; SW SE from base TWO by 2; N from
      base ACE by 1. S from base ACE by 2. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundations_: stack.SS_Foundation (base_rank=KING,
       max_cards=1), stack.SS_Foundation, stack.SS_Foundation
       (base_rank=TWO, dir=2, max_cards=6), stack.SS_Foundation (dir=2,
       max_cards=6)
    :cvar Stack _row_: stack.OpenStack (max_accept=0)
    """
    __info__ = dict(id=638, name='Royal Rendezvous', game_type=GT.TWO_DECK,
                    decks=2, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundations_ = (StackWrapper(stack.SS_Foundation, base_rank=cs.KING,
                                  max_cards=1),
                     stack.SS_Foundation,
                     StackWrapper(stack.SS_Foundation, base_rank=cs.TWO,
                                  dir=2, max_cards=6),
                     StackWrapper(stack.SS_Foundation, dir=2,
                                  max_cards=6),)
    _row_ = StackWrapper(stack.OpenStack, max_accept=0)

    def _create_game(self, rows=16, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + 19 * l.XS / 2, l.YM + 9 * l.YS / 2)
        ddx, ddy = l.XM + 3 * l.XS / 2, l.YM
        assert len(self._foundations_) == 4
        suits = l.SUITS * 2
        suits.sort()
        for _foundation_, suit, dx, dy in zip(self._foundations_ * 4, suits,
                          (0, 2, 0, 2, 1, 3, 1, 3, 6, 4, 6, 4, 7, 5, 7, 5,),
                          (0, 0, 1, 1) * 4):
            x, y = l.delta_coords(self, x=ddx, y=ddy, dx=dx, dy=dy)
            s['foundations'].append(_foundation_(x, y, game=self, suit=suit))
        y = l.YM + 3 * l.YS / 2
        for i in range(rows / 8):  # Vert
            x = ddx
            for j in range(i):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return TWO of suit and all ACE on top by rank, suit, deck
        for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneTwoAllAce)

    def _start(self, **kw):
        foundations = self.s['foundations']
        self.s['talon'].dealRow(rows=foundations[1::2], frames=0)  # deal ACE
        self.s['talon'].dealRow(rows=foundations[2::4], frames=0)  # deal TWO
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _waste_.

        *Parameters*:
           stack : Stack
             pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'])


class ShadyLanes(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz. Init deal 1 card. Build down in rank by alt color. Play
      top. Gap only from _reserve_.

    _reserve_
      Pile 4 vert E. Init deal 1 card. Build none. Play top. Gap 1 card from
      _waste_.

    _talon_
      Pile 1 E. Deal _waste_ 1 card.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 horz N _row_. Build up in rank by same color. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SC_Foundation
    :cvar Stack _row_: ShadyLanes_RowStack (max_move=1)
    :cvar Stack _reserve_: stack.OpenStack (max_accept=0)
    :cvar Hint _hint_: ShadyLanes_Hint
    """
    __info__ = dict(id=639, name='Shady Lanes', game_type=GT.TWO_DECK,
                    decks=2, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SC_Foundation
    _row_ = StackWrapper(ShadyLanes_RowStack, max_move=1)
    _reserve = StackWrapper(stack.OpenStack, max_accept=0)
    _hint_ = ShadyLanes_Hint

    def _create_game(self, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + 8 * l.XS, l.YM + 5 * l.YS)
        x, y = l.XM, l.YM
        for suit in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self, suit=suit,
                                 color=suit / 2))
            x += l.XS
        x, y = l.XM, l.YM + l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        x, y = l.XM + 2 * l.XS, l.YM + l.YS
        for i in range(4):
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x, y = self.width - l.XS
        for i in range(4):
            s['reserves'].append(self._reserve_(x, y, game=self))
            y += l.YS
        l.defaultStackGroups()
        return l

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _reserve_ when gap from _waste_.

        *Parameters*:
           stack : Stack
             pile just played
        """
        fill.fromWaste(self, stack, self.s['reserves'])

    def _start(self, **kw):
        talon = self.s['talon']
        self._play_sound('deal')
        talon.dealRow(rows=self.s['reserves'])
        talon.dealRow()
        talon.deal_cards()


class FourWinds(game.Game):
    """
    Finish cards on _foundation_ within 2 turns.

    _row_
      Pile 16 over 4 sides. Init deal 1 card. Each side build 1 card by same
      suit: Clubs W; Hearts N ; Spades E; Diamonds S. Play top.

    _talon_
      Pile 1 Center. Deal _waste_ 1 card.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 4 W N E S. Init deal base ACE 4. Build up in rank by same suit.
      Play none.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (max_move=0)
    :cvar Stack _row_: SS_Reserve
    """
    __info__ = dict(id=675, name='Four Winds', game_type=GT.ONE_DECK,
                    decks=1, redeals=1, skill_level=SL.BALANCED,
                    version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = StackWrapper(stack.WasteTalon, max_rounds=2)
    _waste_ = stack.WasteStack
    _foundation_ = StackWrapper(stack.SS_Foundation, max_move=0)
    _row_ = SS_Reserve
    _reserve = StackWrapper(stack.OpenStack, max_accept=0)

    def _create_game(self, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + 9 * l.XS, l.YM + 6 * l.YS)
        piles = range(2, l.NSUIT + 2)
        for suit, deltas in zip(l.SUIT, (zip(piles, (1,) * l.NSUIT),
                                         zip((1,) * l.NSUIT, piles),
                                         zip(piles, (6,) * l.NSUIT),
                                         zip((6,) * l.NSUIT, piles))):
            for dx, dy in deltas:
                x, y = l.delta_coords(dx=dx, dy=dy)
                s['rows'].append(self._row_(x, y, game=self, base_suit=suit))
        piles = range(l.NDECKS + l.NSUIT / 2, 2 * l.NDECKS + l.NSUIT / 2)
        for suit, deltas in zip(l.SUIT, (zip(piles, (0,) * l.NDECKS),
                                         zip((0,) * l.NDECKS, piles),
                                         zip(piles, (14,) * l.NDECKS),
                                         zip((14,) * l.NDECKS, piles))):
            for dx, dy in deltas:
                x, y = l.delta_coords(dx=dx, dy=dy, fx=2, fy=2)
                s['foundations'].append(self._foundation_(x, y, game=self,
                                                       suit=suit))
        x, y = (self.width - 2 * l.XS) / 2, (self.height - l.YS) / 2
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        l.set_rounds_text(self.s['talon'], 'nn')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 's')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class BoxingTheCompass(FourWinds):
    """
    Finish cards on _foundation_ within 2 turns.

    _row_
      Pile 16 over 4 sides. Init deal 1 card. Each side build 1 card by same
      suit: Clubs W; Hearts N ; Spades E; Diamonds S. Play top.

    _talon_
      Pile 1 Center. Deal _waste_ 1 card.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 W N E S. Init deal base ACE 8. Build up in rank by same suit.
      Play none.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (max_move=0)
    :cvar Stack _row_: SS_Reserve
    """
    __info__ = dict(id=676, name='Boxing the Compass', game_type=GT.TWO_DECK,
                    decks=2, redeals=1, skill_level=SL.BALANCED,
                    version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70


class Colonel(game.Game):
    """
    Finish cards on _foundation_ within 3 turns.

    _row_
      Pile 36 over 3 row. Init deal 1 card. Build down in rank by same suit
      from S with cards else S row. Gap N play 1st. Play S no cards.

    _talon_
      Pile 1 SE. Deal _waste_ 1 card.

    _waste_
      Pile 1 W of _talon_. Init deal 1 card. No build. Top card play.

    _foundation_
      Pile 8 N _row_. Build up in rank by same suit. No card play.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (max_move=0)
    :cvar Stack _row_: Colonel_RowStack (max_move=1)
    :cvar Hint _hint_: Colonel_Hint
    """
    __info__ = dict(id=693, name='Colonel', game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = StackWrapper(stack.SS_Foundation, max_move=0)
    _row_ = StackWrapper(Colonel_RowStack, max_move=1)
    _hint_ = Colonel_Hint

    def _create_game(self, rows=(12,) * 3, **kw):
        l, s = Layout(self), self.s
        suits = l.NSUITS
        foundations = len(suits)
        max_rows = max(max(rows), foundations)
        self.table_size = (l.XM + max_rows * l.XS, l.YM + 5 * l.YS)
        center = lambda p: l.XM + (max_rows - p) * l.XS / 2
        x, y = center(foundations), l.YM
        for suit in suits:
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
            x += l.XS
        y += l.YS
        for row in rows:
            x = center(row)
            for j in range(row):
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                x += l.XS
            y += l.YS
        x = center(2)
        y += l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'se')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow(frames=4)
        self.s['talon'].deal_cards()


class TheRedAndTheBlack(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz. Init deal 1 card. Build down in rank by alt color. Play
      top.

    _reserve_
      Pile 4 horz S _row_. Build 1 card from _waste_. Play top.

    _talon_
      Pile 1 E. Deal _waste_ 1 card.

    _waste_
      Pile 1 W of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 horz N _row_. Init deal base ACE 8. Build up in rank by alt
      color from _row_ or _waste_. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: TheRedAndTheBlack_Foundation
    :cvar Stack _row_: TheRedAndTheBlack_RowStack (max_move=1)
    :cvar Stack _reserve_: fromWaste_Reserve
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=695, name='The Red and the Black',
                    game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.BALANCED, version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = TheRedAndTheBlack_Foundation
    _row_ = StackWrapper(TheRedAndTheBlack_RowStack, max_move=1)
    _reserve_ = fromWaste_Reserve
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=4, reserves=4, **kw):
        l, s = Layout(self), self.s
        max_rows = max(l.NSUITS, rows, reserves)
        self.table_size = (l.XM + max_rows * l.XS, l.YM + 9 * l.YS / 2)
        center = lambda p: l.XM + (max_rows - p) * l.XS / 2
        x, y = center(l.NSUITS), l.YM
        for suit in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
            x += l.XS
        x = center(rows)
        y += l.YS
        for i in range(rows):
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            x += l.XS
        x = center(reserves)
        y += l.YS
        for i in range(reserves):
            s['reserves'].append(self._reserve_(x, y, game=self))
            x += l.XS
        x = center(2)
        y += 3 * l.YS / 2
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'se')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].deal_cards()

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)


class TwilightZone(TheRedAndTheBlack):
    """
    Finish cards on _foundation_ within 2 turns.

    _row_
      Pile 4 horz. Init deal 1 card. Build down in rank by alt color from
       _waste_. Play top. Gap deal from pile S on _reserve_.

    _reserve_
      Pile 4 horz S _row_. Init deal 1 card. Build none. Play top. Gap deal
      from _waste_ or _talon_.

    _talon_
      Pile 1 E. Play top. Gap reset _waste_.

    _waste_
      Pile 1 W _talon_. Build from _talon_. Play top.

    _foundation_
      Pile 8 horz N _row_. Build up in rank by alt color from _row_ or
      _waste_. Play top.

    note::
      ⟪autodeal⟫ _waste_ disabled.
    ------
    :cvar Stack _talon_: TwilightZone_Talon (max_move=1, max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack (max_accept=1)
    :cvar Stack _foundation_: TwilightZone_Foundation
    :cvar Stack _row_: TwilightZone_RowStack (max_move=1)
    :cvar Stack _reserve_: stack.OpenStack
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=748, name='Twilight Zone', game_type=GT.TWO_DECK,
                    decks=2, redeals=1, skill_level=SL.BALANCED,
                    version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = StackWrapper(TwilightZone_Talon, max_move=1, max_rounds=2)
    _waste_ = StackWrapper(stack.WasteStack, max_accept=1)
    _foundation_ = TwilightZone_Foundation
    _row_ = StackWrapper(TwilightZone_RowStack, max_move=1)
    _reserve_ = stack.OpenStack
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        l, s = TheRedAndTheBlack._create_game(self, **kw), self.s
        self.sg['dropstacks'] += [s['talon']]
        self.sg['openstacks'] += s['wastes']
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].fill_stack()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _reserve_ neighbor and stack if
        _reserve_ when gap from _waste_ else _talon_.

        *Parameters*:
           stack : Stack
             pile just played
        """
        if not stack.cards:
            s = self.s
            if stack in s['rows']:
                from_stack = s['reserves'][s['rows'].index(stack)]
                if from_stack.cards:
                    from_stack.move_pile(1, stack)
            elif stack in s['reserves']:
                from_stack = (s['wastes'][-1] if s['wastes'][-1].cards
                                              else s['talon'])
                if from_stack.cards:
                    from_stack.move_pile(1, stack)

    def _auto_deal(self, sound=True):
        return 0


PROVIDES = [RoyalCotillion, OddAndEven, Kingdom, Alhambra, Carpet,
            BritishConstitution, NewBritishConstitution, Twenty, Granada,
            GrantsReinforcement, RoyalRendezvous, ShadyLanes, FourWinds,
            BoxingTheCompass, Colonel, TheRedAndTheBlack, TwilightZone,
            ThreePirates, Reserves, Frames]